#include "bookitemlist.h"
#include "book.h"
#include "bookunzipper.h"
#include <QtDeclarative/qdeclarative.h>
#include <QDirIterator>
#include "quazipfile.h"
#include "quazip_global.h"
#include <QImageReader>
#include "bookunrar.h"
#include <QTimer>

BookItemList::BookItemList(QDeclarativeItem *parent):
        QDeclarativeItem(parent)
{
    // By default, QDeclarativeItem does not draw anything. If you subclass
    // QDeclarativeItem to create a visual item, you will need to uncomment the
    // following line:

    // setFlag(ItemHasNoContents, false);

    qRegisterMetaType<BookItemList::ErrorCodes>("ErrorCodes");

    cleanDirectories(CleanAll);
    createPrivateDirectories();

    m_unzipper = new BookUnzipper;
    connect(m_unzipper,SIGNAL(extractFinished(BookItemList::ErrorCodes)),this,SLOT(onExtractingFinished(BookItemList::ErrorCodes)));

    m_unrar = new BookUnrar(this);
    connect(m_unrar,SIGNAL(extractFinished(BookItemList::ErrorCodes)),this,SLOT(onExtractingFinished(BookItemList::ErrorCodes)));
    connect(m_unrar,SIGNAL(firstImageExtracted(BookItemList::ErrorCodes,Book*)),this,SLOT(onFirstImageExtracted(BookItemList::ErrorCodes,Book*)));
}

BookItemList::~BookItemList()
{
    m_unzipper->cancelRun();

    cleanDirectories(CleanAll);
    while(!m_books.isEmpty()){
        Book* b = m_books.takeLast();
        delete b;
    }
    m_books.clear();

    delete m_unzipper;
}

void BookItemList::refreshBooks()
{
    cleanDirectories(CleanAll);
    createPrivateDirectories();

    while(!m_books.isEmpty()){
        Book* b = m_books.takeLast();
        delete b;
    }
    m_books.clear();

    QTimer::singleShot(0,this,SLOT(searchArchives()));
}

void BookItemList::cleanDirectories(CleanDirectories toClean)
{
    switch(toClean){
    case Extracted:{
        QDir dir(PRIVATE_BOOKDIR_EXTRACTED);
        QDirIterator fiter(PRIVATE_BOOKDIR_EXTRACTED,QStringList()<<"*",QDir::Files,QDirIterator::Subdirectories);
        QDirIterator diter(PRIVATE_BOOKDIR_EXTRACTED,QDir::AllDirs,QDirIterator::Subdirectories);

        while(fiter.hasNext()){
            QString name = fiter.next();
            QFile::remove(name);
        }

        while(diter.hasNext()){
            QString name = diter.next();
            if(!name.endsWith('.') && !name.endsWith("..")){
                QDir dir(name);
                dir.rmpath(name);
            }
        }

        dir.rmpath(PRIVATE_BOOKDIR_EXTRACTED);
    }break;
    case Thumbs:{
        QDir dir(PRIVATE_BOOKDIR_THUMBS);
        QDirIterator fiter(PRIVATE_BOOKDIR_THUMBS,QStringList()<<"*",QDir::Files,QDirIterator::Subdirectories);
        QDirIterator diter(PRIVATE_BOOKDIR_THUMBS,QDir::AllDirs,QDirIterator::Subdirectories);

        while(fiter.hasNext()){
            QString name = fiter.next();
            QFile::remove(name);
        }

        while(diter.hasNext()){
            QString name = diter.next();
            if(!name.endsWith('.') && !name.endsWith("..")){
                QDir dir(name);
                dir.rmpath(name);
            }
        }

        dir.rmpath(PRIVATE_BOOKDIR_THUMBS);
    }break;

    case CleanAll:{
        QDir dir(PRIVATE_BOOKDIR);
        QDirIterator fiter(PRIVATE_BOOKDIR,QStringList()<<"*",QDir::Files,QDirIterator::Subdirectories);
        QDirIterator diter(PRIVATE_BOOKDIR,QDir::AllDirs,QDirIterator::Subdirectories);

        while(fiter.hasNext()){
            QString name = fiter.next();
            QFile::remove(name);
        }

        while(diter.hasNext()){
            QString name = diter.next();
            if(!name.endsWith('.') && !name.endsWith("..")){
                QDir dir(name);
                dir.rmpath(name);
            }
        }

        dir.rmpath(PRIVATE_BOOKDIR);
    }break;

    default:break;
    }
}

void BookItemList::createPrivateDirectories()
{
    QDir dir(PRIVATE_BOOKDIR);
    dir.mkpath(PRIVATE_BOOKDIR);
    dir.setPath(PRIVATE_BOOKDIR_EXTRACTED);
    dir.mkpath(PRIVATE_BOOKDIR_EXTRACTED);
    dir.setPath(PRIVATE_BOOKDIR_THUMBS);
    dir.mkpath(PRIVATE_BOOKDIR_THUMBS);
}

QDeclarativeListProperty<Book> BookItemList::books()
{
    return QDeclarativeListProperty<Book>(this,0,NULL,&BookItemList::countFunction,&BookItemList::atFunction,NULL);
}

Book* BookItemList::atFunction(QDeclarativeListProperty<Book> *property, int index)
{
    BookItemList* list = qobject_cast<BookItemList*>(property->object);
    if(list){
        return list->m_books.at(index);
    }else{
        return NULL;
    }
}

int BookItemList::countFunction(QDeclarativeListProperty<Book> *property)
{    
    BookItemList* list = qobject_cast<BookItemList*>(property->object);
    if(list){
        return list->m_books.count();
    }else{
        return 0;
    }
}


void BookItemList::openBook(int index)
{
    Book* b = NULL;
    b = m_books.at(index);
    m_openedBookIndex=-1;

    if(b!=NULL){
        cleanDirectories(Extracted);
        m_openedBookIndex = index;
        switch(b->getArchiveType()){

        case Book::Zip:{
            m_unzipper->setArchiveName(b->getBookArchiveName());
            m_unzipper->start();
        }break;

        case Book::Rar:{
            m_unrar->setArchiveName(b->getBookArchiveName());
            m_unrar->extractAll();
        }break;

        default:break;
        }
    }
}

void BookItemList::onExtractingFinished(BookItemList::ErrorCodes code)
{
    Book* book = m_books.at(m_openedBookIndex);
    bool success = false;
    if(code==NoError){        
        QList<QByteArray> arr = QImageReader::supportedImageFormats();
        QStringList formats;
        foreach(QByteArray ar, arr){
            QString fmt("*.%1");
            fmt = fmt.arg(QVariant(ar).toString());
            formats.append(fmt);
        }

        QDirIterator diter(PRIVATE_BOOKDIR_EXTRACTED,formats,QDir::Files,QDirIterator::Subdirectories);
        QStringList files;
        while(diter.hasNext()){
            QString name = diter.next();
            files.append(name);
        }

        if(!files.isEmpty()){
            files.sort();
            book->setFilenameList(files);
        }
        success = true;
    }
    emit bookOpen(success, book);
}

void BookItemList::searchArchives()
{
    QDirIterator diter(ARCHIVE_DIRECTORY,QStringList()<<"*.cbz"<<"*.zip",QDir::Files,QDirIterator::Subdirectories);
    QDirIterator riter(ARCHIVE_DIRECTORY,QStringList()<<"*.rar"<<"*.cbr",QDir::Files,QDirIterator::Subdirectories);
    QStringList files;
    QStringList rarFiles;
    QList<QByteArray> sported = QImageReader::supportedImageFormats();
    QDir thumbs(PRIVATE_BOOKDIR_THUMBS);
    thumbs.mkpath(PRIVATE_BOOKDIR_THUMBS);

    while(diter.hasNext()){
        files.append(diter.next());
    }
    while(riter.hasNext()){
        rarFiles.append(riter.next());
    }

    if(!files.isEmpty()){
        foreach(QString fn, files){            
            QuaZip zip(fn);
            if(zip.open(QuaZip::mdUnzip)){
                bool found = false;
                QList<QuaZipFileInfo> files = zip.getFileInfoList();

                foreach(QuaZipFileInfo imgfile, files){                    
                    foreach(QByteArray supported, sported ){
                        QString extension(supported);
                        QString fname = imgfile.name;
                        if( fname.endsWith(extension, Qt::CaseInsensitive)){
                            found = true;
                            Book* b = new Book();

                            int start = fn.lastIndexOf('/');
                            int end = fn.lastIndexOf('.');

                            QString bname = fn.mid(start+1,end-start-1);
                            b->setBookName(bname);
                            b->setBookArchiveName(fn);

                            zip.setCurrentFile(fname);

                            QuaZipFile file(&zip);

                            if(file.open(QIODevice::ReadOnly)){
                                QImage img;
                                QString imgname = bname;
                                imgname = imgname.replace(QRegExp("\\s"),QString("_"));
                                imgname = imgname.replace(QRegExp("\\W"),QString("_"));
                                imgname = PRIVATE_BOOKDIR_THUMBS+"/"+imgname+".png";
                                img.loadFromData(file.readAll());
                                img = img.scaled(SCALED_ICON_SIZE);
                                img.save(imgname);

                                b->setListImage(QUrl(imgname));
                                file.close();
                            }
                            m_books.append(b);
                        }
                    }
                    if(found)
                        break;
                }
                zip.close();
            }
        }
    }

    m_bookCount = m_books.count();
    if(!rarFiles.isEmpty()){
        m_bookCount+=rarFiles.count();
        foreach(QString fname, rarFiles){
            Book* b = new Book();

            int start = fname.lastIndexOf('/');
            int end = fname.lastIndexOf('.');

            QString bname = fname.mid(start+1,end-start-1);
            b->setBookName(bname);
            b->setBookArchiveName(fname);

            m_unrar->getFirstImage(b);
        }
    }

    qDebug() << "bookcount: " << m_bookCount << " mbooks list: " << m_books.count();

    if(!m_books.isEmpty() && rarFiles.isEmpty())
        emit this->refreshBooksDone(true);
    else if(m_books.isEmpty() && rarFiles.isEmpty())
        emit this->refreshBooksDone(false);
}

void BookItemList::onFirstImageExtracted(BookItemList::ErrorCodes code, Book* book)
{
    qDebug() << "Error: " <<code;
    if(code == NoError){
        m_books.append(book);
    }else{
        m_bookCount--;
        delete book;
    }
    if(m_books.count() == m_bookCount){
        emit this->refreshBooksDone(true);
    }
}
